use std::fmt::Debug;

pub struct Protocol{

}
#[derive(Debug,Clone)]
pub enum Command{
    Connect=0x01,//连接
    ConnAck=0x02,//连接确认

    Subscribe=0x03,//订阅主题
    SubAck=0x04,//订阅确认
    UnSubscribe=0x05,//取消订阅主题
    UnSubAck=0x06,//取消订阅确认

    Publish=0x07,//发布消息
    PubAck=0x08,//发布确认
    PubRec=0x09,//发布消息第一次确认,QoS=2
    PubRel=0x0A,//发布消息第二次确认,QoS=2
    PubComp=0x0B,//发布消息第三次确认,QoS=2
    SendOnlyOne=0x0C,//（负载均衡）在主题内选择一个客户端传送
    
    P2P=0xF0,//点对点发送
    P2PAck=0xF1,//点对点发送确认

    DisConnect=0xFD,//告知客户端断开，不允许自动重连
    PingReq=0xFE,//心跳请求
    PingResp=0xFF,//心跳响应
}
impl Protocol {
    pub fn new() -> Self {
        Protocol {}
    }
    pub fn Analyze(&self,addr:&String, data: &Vec<u8>) -> bool {
        let cmd = data[0];
        match cmd {
            0x01 => {
                        let s = self.encode_login(1234567, "Shexking".to_string(), "23232sdsafsafDDEF@dsfds#".to_string());
                        let (id,name,pwd) = self.decode_login(&s);
                        println!("Connect ID={},Name={},Pwd={}",id,name,pwd);
                    }
            0x02 => {
                        println!("ConnAck");
                    }
            0x03 => {
                        println!("Subscribe");
                    }
            0x04 => {
                        println!("SubAck");
                    }
            0x05 => {
                        println!("UnSubscribe");
                    }
            0x06 => {
                        println!("UnSubAck");
                    }
            0x07 => {
                        println!("Publish");
                    }
            0x08 => {
                        println!("PubAck");
                    }
            0x09 => {
                        println!("PubRec");
                    }
            0x0A => {
                        println!("PubRel");
                    }
            0x0B => {
                        println!("PubComp");
                    }
            0x0C => {
                        println!("SendOnlyOne");
                    }
            0xF0 => {
                        println!("P2P");
                    }
            0xF1 => {
                        println!("P2PAck");
                    }
            0xFD => {
                        println!("DisConnect");
                    }
            0xFE => {
                        println!("PingReq");
                    }
            0xFF => {
                        println!("PingResp");
                    }
            _ => {
                        println!("Unknown Command:{}", cmd);
                    }
        }
        true
    }
    fn get_u16(vec: &[u8], index: usize) -> Option<u16> {
        vec.get(index..index + 2)
        .and_then(|slice| slice.try_into().ok())
        .map(u16::from_be_bytes)
    }
    fn get_u32(vec: &[u8], index: usize) -> Option<u32> {
        vec.get(index..index + 4)
        .and_then(|slice| slice.try_into().ok())
        .map(u32::from_be_bytes)
    }
    fn get_u64(vec: &[u8], index: usize) -> Option<u64> {
        vec.get(index..index + 8)
        .and_then(|slice| slice.try_into().ok())
        .map(u64::from_be_bytes)
    }
    
    fn decode_login(&self,data: &Vec<u8>)->(u64,String,String){
        //Body:id(u64,8byte)+namelen(u16,2byte)+name(nbyte,n=namelen)+pwdlen(u16,2byte)+pwd(nbyte,n=pwdlen)
        let mut index = 0;
        let _cmd = data[index];
        index += 1;
        let id = Self::get_u64(data, index).unwrap();
        index += 8;
        let namelen = Self::get_u16(data, index).unwrap();
        index += 2;
        let name = std::str::from_utf8(&data[index..index+(namelen as usize)]).unwrap();
        index += namelen as usize;
        let pwdlen = Self::get_u16(data, index).unwrap();
        index += 2;
        let pwd = std::str::from_utf8(&data[index..index+(pwdlen as usize)]).unwrap();        

        (id,name.to_string(),pwd.to_string())
    }
    fn encode_login(&self,id:u64,name:String,pwd:String)->Vec<u8>{
        let mut data:Vec<u8> = Vec::new();
        data.push(0x01);
        data.extend_from_slice(&id.to_be_bytes().to_vec());
        data.extend_from_slice(&(name.len() as u16).to_be_bytes().to_vec());
        data.extend_from_slice(&name.as_bytes());
        data.extend_from_slice(&(pwd.len() as u16).to_be_bytes().to_vec());
        data.extend_from_slice(&pwd.as_bytes());
        data.to_vec()        
    }
}
